import * as R from 'ramda'
import * as vsc from 'vscode'
import { 获得当前输入字段, 获得系统补全项, log } from './VSC扩展'
import { 上下文, 创建上下文, 查询锁, 设置锁 } from './上下文'
import { 字典组拼写转换 } from './拼写转换'

// 程序运行中的上下文.
var 上下文: 上下文

async function 完成项提供者(document: vsc.TextDocument, position: vsc.Position) {
  // 必要变量
  var 补全项: vsc.CompletionItem[] = []
  var 输入字段 = await 获得当前输入字段()

  log(
    `开始补全 ${document.fileName}, ${document.languageId}, ${position.line}:${position.character}, 输入字段: ${输入字段}`,
  )
  // 这个插件的工作原理是:
  // - 通过`输入字段`获得一系列中文, 具体的方法在后面讨论.
  // - 将这些中文封装为补全项, 并设置几个重要的属性:
  //   - filterText: 用来判断这个补全项是否会出现在提示框中, 如果此属性的开头和当前正在输入的词一致, 则此补全项会出现在提示框中.
  //     - 这里的`正在输入的词`的计算方式和`输入字段`的计算方法一致.
  //   - sortText: 若此补全项出现在提示框中, 此属性决定排序, 此属性和输入字段可以完全无关.
  //   - label: 若此补全项出现在提示框中, 此属性是显示在提示框中的具体内容.
  //   - insertText: 若选择此补全项, 此属性决定实际插入的内容.

  // 调用'获得系统补全项'时会调用'完成项提供者'函数, 这会导致无限循环.
  // 这里判断, 如果是无限循环的场景, 就直接返回空数组.
  if (查询锁(上下文.递归锁) === true) return []

  // 使用vsc补全项
  // 命令`executeCompletionItemProvider`的结果在各语言中不同:
  // - 对于js, ts等语言, `executeCompletionItemProvider`会返回所有关键词, 不管这个词是否和输入有关.
  // - 对于c, c++, python等语言, 取决于插件实现, 通常`executeCompletionItemProvider`不会返回所有关键词, 只会返回和输入相关的关键词.
  上下文.递归锁 = 设置锁(上下文.递归锁, true)
  var 补全项提供器 = await 获得系统补全项(document.uri, position)
  上下文.递归锁 = 设置锁(上下文.递归锁, false)

  补全项 = 补全项.concat(补全项提供器.items)
  // 做以下处理:
  // - 去重.
  // - 过滤不包含中文的补全项.
  // - 过滤现在正在输入的字段.
  // - 过滤自定义的片段(Snippet), 因为无论这个函数是否返回结果, vsc总会带上它们.
  补全项 = R.uniqWith((a, b) => a.label === b.label, 补全项)
    .filter((a) => /[\u4e00-\u9fa5\u3007]/.test(a.label.toString())) // 包含中文
    .filter((a) => a.label != 输入字段)
    .filter((a) => a.kind != vsc.CompletionItemKind.Snippet)

  // 设置最终结果
  for (var a of 补全项) {
    var 所有拼写 = 字典组拼写转换(上下文.拼写字典, a.label.toString())
    log(`${a.label.toString()} 所有拼写: ${所有拼写}`)

    // `所有拼写`里包括多音字, 比如对"多重"而言, 会返回['duochong', 'duozhong'].
    // 如果分别把两个结果都包装成补全项, 就会出现重复的结果.
    // 如果将其包装成一个结果, 使用'duochong,duozhong'作为提示词, 当用户输入'duo'的时候没问题, 用户输入'duoz'的时候, 因为vscode的机制,结果也能出来.
    a.filterText = 所有拼写.join(',') + a.label
    a.insertText = a.label.toString()
  }

  return new vsc.CompletionList(补全项, true)
}


export async function activate(context: vsc.ExtensionContext) {
  log('中文代码输入: 插件已启动')

  上下文 = await 创建上下文()

  // 当配置修改时刷新配置
  vsc.workspace.onDidChangeConfiguration(async () => {
    上下文 = await 创建上下文()
  })

  context.subscriptions.push(
    vsc.languages.registerCompletionItemProvider(
      [
        { scheme: 'file', language: '*' },
        { scheme: 'untitled', language: '*' },
        { scheme: 'file', language: '*', notebookType: '*' },
        { scheme: 'untitled', language: '*', notebookType: '*' },
      ],
      { provideCompletionItems: 完成项提供者, resolveCompletionItem: () => null },
      ...["$", ":"],
    ),
  )
}

export function deactivate() { }
